﻿////////////////////////////////////////////////////////////////////////////////////////////////////
//
// VSCoverageToEmma - Visual Studio Coverage file to Emma converter
// Copyright (c) Kouji Matsui, All rights reserved.
//
// * Redistribution and use in source and binary forms, with or without modification,
//   are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright notice,
//   this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
////////////////////////////////////////////////////////////////////////////////////////////////////

using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;

namespace System.Linq
{
	public static class Enumerable
	{
		public static IEnumerable<U> Select<T, U>(this IEnumerable<T> enumerable, Func<T, U> selector)
		{
			Debug.Assert(enumerable != null);
			Debug.Assert(selector != null);

			foreach (var value in enumerable)
			{
				yield return selector(value);
			}
		}

		public static IEnumerable<V> SelectMany<T, U, V>(
			this IEnumerable<T> enumerable,
			Func<T, IEnumerable<U>> collectionSelector,
			Func<T, U, V> resultSelector)
		{
			Debug.Assert(enumerable != null);
			Debug.Assert(collectionSelector != null);
			Debug.Assert(resultSelector != null);

			foreach (var value in enumerable)
			{
				var results = collectionSelector(value);
				Debug.Assert(results != null);

				foreach (var innerValue in results)
				{
					yield return resultSelector(value, innerValue);
				}
			}
		}

		public static IEnumerable<T> Where<T>(this IEnumerable<T> enumerable, Func<T, bool> selector)
		{
			Debug.Assert(enumerable != null);
			Debug.Assert(selector != null);

			foreach (var value in enumerable)
			{
				if (selector(value) == true)
				{
					yield return value;
				}
			}
		}

		public static IEnumerable<T> OrderBy<T, U>(this IEnumerable<T> enumerable, Func<T, U> selector)
		{
			Debug.Assert(enumerable != null);
			Debug.Assert(selector != null);

			var list = new List<T>(enumerable);
			list.Sort((value0, value1) => Comparer<U>.Default.Compare(selector(value0), selector(value1)));
			return list;
		}

		public static T First<T>(this IEnumerable<T> enumerable)
		{
			Debug.Assert(enumerable != null);

			foreach (var value in enumerable)
			{
				return value;
			}

			throw new InvalidOperationException();
		}

		public static int Sum(this IEnumerable<int> enumerable)
		{
			Debug.Assert(enumerable != null);

			var result = 0;
			foreach (var value in enumerable)
			{
				result += value;
			}

			return result;
		}

		public static string Join(this IEnumerable<string> enumerable, string joinString)
		{
			Debug.Assert(enumerable != null);
			Debug.Assert(string.IsNullOrEmpty(joinString) == false);

			var sb = new StringBuilder();
			var first = true;
			foreach (var value in enumerable)
			{
				if (first == false)
				{
					sb.Append(joinString);
				}
				else
				{
					first = false;
				}

				sb.Append(value);
			}

			return sb.ToString();
		}

		public static Dictionary<U, V> ToDictionary<T, U, V>(
			this IEnumerable<T> enumerable,
			Func<T, U> keySelector,
			Func<T, V> valueSelector)
		{
			Debug.Assert(enumerable != null);
			Debug.Assert(keySelector != null);
			Debug.Assert(valueSelector != null);

			var dictionary = new Dictionary<U, V>();
			foreach (var value in enumerable)
			{
				dictionary.Add(keySelector(value), valueSelector(value));
			}

			return dictionary;
		}

		public static IEnumerable<T> Cast<T>(this IEnumerable enumerable)
		{
			foreach (var value in enumerable)
			{
				yield return (T)value;
			}
		}

		public static List<T> ToList<T>(this IEnumerable<T> enumerable)
		{
			return new List<T>(enumerable);
		}

		public static T[] ToArray<T>(this IEnumerable<T> enumerable)
		{
			return new List<T>(enumerable).ToArray();
		}

		public static U GetValue<T, U>(this IDictionary<T, U> dictionary, T key, U defaultValue)
		{
			U value;
			if (dictionary.TryGetValue(key, out value) == true)
			{
				return value;
			}
			else
			{
				return defaultValue;
			}
		}

		public static IEnumerable<T> Distinct<T>(this IEnumerable<T> enumerable)
		{
			var dictionary = new Dictionary<T, T>();
			foreach (var value in enumerable)
			{
				if (dictionary.ContainsKey(value) == false)
				{
					dictionary.Add(value, value);
					yield return value;
				}
			}
		}

		public static int Count(this IEnumerable enumerable)
		{
			var collection = enumerable as ICollection;
			if (collection != null)
			{
				return collection.Count;
			}

			var count = 0;
			foreach (var value in enumerable)
			{
				count++;
			}

			return count;
		}

		public static IEnumerable<IGrouping<T, U>> GroupBy<T, U>(this IEnumerable<U> enumerable, Func<U, T> keySelector)
		{
			var results = new Dictionary<T, Grouping<T, U>>();

			foreach (var value in enumerable)
			{
				var key = keySelector(value);

				Grouping<T, U> grouping;
				if (results.TryGetValue(key, out grouping) == false)
				{
					grouping = new Grouping<T, U>(key);
					results.Add(key, grouping);
				}

				grouping.Add(value);
			}

			return results.Values.Cast<IGrouping<T, U>>();
		}

		public static IEnumerable<T> AsEnumerable<T>(this IEnumerable<T> enumerable)
		{
			return enumerable;
		}

		public static IEnumerable<U> SelectMany<T, U>(this IEnumerable<T> enumerable, Func<T, IEnumerable<U>> valueSelector)
		{
			foreach (var inner in enumerable)
			{
				foreach (var value in valueSelector(inner))
				{
					yield return value;
				}
			}
		}

		private sealed class Grouping<T, U> : List<U>, IGrouping<T, U>
		{
			public Grouping(T key)
			{
				this.Key = key;
			}

			public T Key
			{
				get;
				private set;
			}
		}
	}
}
